---
title: 图像服务 API
i18nReady: true
---
import Since from '~/components/Since.astro';

`astro:assets` 旨在使任何图像优化服务都能轻松地在 Astro 上构建服务。

## 什么是图像服务？

Astro 提供两种类型的图像服务：本地和外部。

- **本地服务**在构建静态站点时直接处理图像转换，或者在运行时同时处理开发模式和按需渲染中的图像转换。这些通常是围绕 Sharp、ImageMagick 或 Squoosh 等库的封装。在开发模式和按需渲染的生产路径中，本地服务使用 API 端点进行转换。
- **外部服务**指向 URL，可以添加对 Cloudinary、Vercel 或任何符合 [RIAPI](https://github.com/riapi/riapi) 的服务器等服务的支持。

## 使用图像服务 API 构建

服务定义采用导出默认对象的形式，具有各种必需的方法（“钩子”）。

外部服务提供一个 `getURL()`，它指向输出 `＜img＞` 标记的 `src`。

本地服务为开发模式和按需渲染提供 `transform()` 方法来对图像执行转换，并提供 `getURL()` 和 `parseURL()` 方法来使用端点。

两种类型的服务都可以提供 `getHTMLAttributes()` 来确定输出 `<img>` 的其他属性，并提供 `validateOptions()` 来验证和增强传递的选项。

### 外部服务

外部服务指向一个远程 URL，用作最终 `<img>` 标记的 `src` 属性。该远程 URL 负责下载、转换和返回图像。

```js
import type { ExternalImageService, ImageTransform, AstroConfig } from "astro";

const service: ExternalImageService = {
  validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']) {
    const serviceConfig = imageConfig.service.config;
    // 强制用户设置最大宽度。
    if (options.width > serviceConfig.maxWidth) {
      console.warn(`Image width ${options.width} exceeds max width ${serviceConfig.maxWidth}. Falling back to max width.`);
      options.width = serviceConfig.maxWidth;
    }

    return options;
  },
  getURL(options, imageConfig) {
    return `https://mysupercdn.com/${options.src}?q=${options.quality}&w=${options.width}&h=${options.height}`;
  },
  getHTMLAttributes(options, imageConfig) {
    const { src, format, quality, ...attributes } = options;
		return {
			...attributes,
			loading: options.loading ?? 'lazy',
			decoding: options.decoding ?? 'async',
		};
	}
};


export default service;
```

### 本地服务

要创建自己的本地服务，可以指向[内置端点](https://github.com/withastro/astro/blob/main/packages/astro/src/assets/endpoint/generic.ts)（`/_image`），或者你还可以创建自己的端点来调用服务的方法。

```js
import type { LocalImageService, AstroConfig } from "astro";

const service: LocalImageService = {
  getURL(options: ImageTransform, imageConfig: AstroConfig['image']) {
    const searchParams = new URLSearchParams();
		searchParams.append('href', typeof options.src === "string" ? options.src : options.src.src);
		options.width && searchParams.append('w', options.width.toString());
		options.height && searchParams.append('h', options.height.toString());
		options.quality && searchParams.append('q', options.quality.toString());
		options.format && searchParams.append('f', options.format);
    return `/my_custom_endpoint_that_transforms_images?${searchParams}`;
    // 或者使用内置端点，它将调用你的 parseURL 和 transform 函数：
    // return `/_image?${searchParams}`;
  },
  parseURL(url: URL, imageConfig) {
    return {
      src: params.get('href')!,
      width: params.has('w') ? parseInt(params.get('w')!) : undefined,
      height: params.has('h') ? parseInt(params.get('h')!) : undefined,
			format: params.get('f'),
      quality: params.get('q'),
    };
  },
  transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig): { data: Uint8Array, format: OutputFormat } {
    const { buffer } = mySuperLibraryThatEncodesImages(options);
    return {
      data: buffer,
      format: options.format,
    };
  },
  getHTMLAttributes(options, imageConfig) {
		let targetWidth = options.width;
		let targetHeight = options.height;
		if (typeof options.src === "object") {
			const aspectRatio = options.src.width / options.src.height;

			if (targetHeight && !targetWidth) {
				targetWidth = Math.round(targetHeight * aspectRatio);
			} else if (targetWidth && !targetHeight) {
				targetHeight = Math.round(targetWidth / aspectRatio);
			}
		}

		const { src, width, height, format, quality, ...attributes } = options;

		return {
			...attributes,
			width: targetWidth,
			height: targetHeight,
			loading: attributes.loading ?? 'lazy',
			decoding: attributes.decoding ?? 'async',
		};
	},
  propertiesToHash: ['src', 'width', 'height', 'format', 'quality'], 
};
export default service;
```

在构建静态站点和预渲染路由时，`<Image />` 和 `getImage(options)` 都会调用 `transform()` 函数。它们分别通过组件属性或 `options` 参数传递选项。转换后的图像将构建到 `dist/_astro` 文件夹中。它们的文件名将包含传递给 `propertiesToHash` 属性的哈希值。此属性是可选的，默认为 `['src', 'width', 'height', 'format', 'quality']`。如果你的自定义图像服务有更多可以更改生成图像的选项，请将它们添加到数组中。

在开发模式和使用适配器进行按需渲染时，Astro 并不提前知道哪些图像需要优化。Astro 使用 GET 端点（默认情况下为 `/_image` ）在运行时处理图像。 `<Image />` 和 `getImage()` 将它们的选项传递给 `getURL()`，后者将返回端点 URL。然后，端点调用 `parseURL()` 并将结果属性传递给 `transform()`。

#### getConfiguredImageService 和 imageConfig

如果你将自己的端点实现为 Astro 端点，则可以使用 `getConfiguredImageService` 和 `imageConfig` 来调用服务的 `parseURL` 和 `transform` 方法并提供服务的配置对象。

要访问图像服务配置（[`image.service.config`](/zh-cn/reference/configuration-reference/#imageservice)），你可以使用 `imageConfig.service.config`。

```ts title="src/api/my_custom_endpoint_that_transforms_images.ts"
import type { APIRoute } from "astro";
import { getConfiguredImageService, imageConfig } from 'astro:assets';

export const GET: APIRoute = async ({ request }) => {
  const imageService = await getConfiguredImageService();

  const imageTransform = imageService.parseURL(new URL(request.url), imageConfig);
  // ... 从 imageTransform.src 获取图像并将其存储在 inputBuffer 中
  const { data, format } = await imageService.transform(inputBuffer, imageTransform, imageConfig);
  return new Response(data, {
			status: 200,
			headers: {
				'Content-Type': mime.getType(format) || ''
      }
    }
  );
}
```

[请参阅内置端点](https://github.com/withastro/astro/blob/main/packages/astro/src/assets/endpoint/generic.ts) 了解完整示例。


## 钩子

### `getURL()`

**本地和外部服务所需**

`getURL(options: ImageTransform, imageConfig: AstroConfig['image']): string`

对于本地服务，该钩子返回生成图像的端点的 URL（在按需渲染和开发模式下）。在构建期间它不可用。 `getURL()` 指向的本地端点可以调用 `parseURL()` 和 `transform()`。

对于外部服务，该钩子返回图像的最终 URL。

对于这两种类型的服务，`options`是用户作为 `<Image />` 组件的属性或 `getImage()` 选项传递的属性。它们属于以下类型：

```ts
export type ImageTransform = {
    // ESM 导入的图像 | 远程/公共图像路径
    src: ImageMetadata | string;
    width?: number;
    height?: number;
    widths?: number[] | undefined;
	    densities?: (number | `${number}x`)[] | undefined;
    quality?: ImageQuality;
    format?: OutputFormat;
    alt?: string;
    [key: string]: any;
};
```


### `parseURL()`

**本地服务所需；外部服务不可用**

`parseURL(url: URL, imageConfig: AstroConfig['image']): { src: string, [key: string]: any}`

该钩子通过 `getURL()` 将生成的 URL 解析成一个对象，该对象具有可供 `transform` 使用的不同属性（在按需渲染和开发模式下）。在构建期间它不可用。

### `transform()`

**仅本地服务需要；外部服务不可用**

`transform(buffer: Uint8Array, options: { src: string, [key: string]: any }, imageConfig: AstroConfig['image']): { data: Uint8Array, format: OutputFormat }`

该钩子转换并返回图像，并在构建过程中被调用以创建最终的资源文件。

你必须返回 `format` 以确保在按需渲染和开发模式下向用户提供正确的 MIME 类型。

### `getHTMLAttributes()`

**本地和外部服务可选**

`getHTMLAttributes(options: ImageTransform, imageConfig: AstroConfig['image']): Record<string, any>`

该钩子根据用户传递的参数 (`options`) 返回用于将图像呈现为 HTML 的所有附加属性。

### `getSrcSet()`

<p><Since v="3.3.0" /></p>

**对于本地和外部服务而言是可选的。**

`getSrcSet?: (options: ImageTransform, imageConfig: AstroConfig['image']): SrcSetValue[] | Promise<SrcSetValue[]>;`

这个钩子函数会生成指定图像的多个变体，例如，在 `<img>` 或 `<picture>` 的 `source` 上生成 `srcset` 属性。

该钩子函数返回一个包含以下属性的对象数组：

```ts
export type SrcSetValue = {
	transform: ImageTransform;
	descriptor?: string;
	attributes?: Record<string, any>;
};
```

### `validateOptions()`

**本地和外部服务可选**

`validateOptions(options: ImageTransform, imageConfig: AstroConfig['image']): ImageTransform`

该钩子允许你验证和增强用户传递的选项。对于设置默认选项或告诉用户参数必传来说，这非常有用。

[查看 Astro 的内置服务中是如何使用 `validateOptions()` 的](https://github.com/withastro/astro/blob/0ab6bad7dffd413c975ab00e545f8bc150f6a92f/packages/astro/src/assets/services/service.ts#L124) 。

## 用户配置

在 `astro.config.mjs` 中配置要使用的图像服务。配置采用以下形式：

```js title="astro.config.mjs"
import { defineConfig } from "astro/config";

export default defineConfig({
  image: {
    service: {
      entrypoint: "your-entrypoint", // 'astro/assets/services/sharp' | string,
      config: {
        // 特定的服务配置，可选。
      }
    }
  },
});
```

## 工具函数

Astro 提供了许多助手函数，可用于部署定制化的图像服务。这些工具函数可使用 `astro/assets/utils` 导入：

```ts
import { 
    isRemoteAllowed,
    matchHostname,
    matchPathname,
    matchPattern,
    matchPort,
    matchProtocol,
    isESMImportedImage,
    isRemoteImage,
    resolveSrc,
    imageMetadata,
    emitESMImage,
    getOrigQueryParams,
    inferRemoteSize,
    propsToFilename,
    hashTransform
} from "astro/assets/utils";
```

### `isRemoteAllowed()`

<p>
**类型：** `(src: string, { domains, remotePatterns }: {domains: string[], remotePatterns: RemotePattern[] }): boolean`<br />
<Since v="4.0.0" />
</p>

判断一个给定的远程资源（由其源 URL 所识别）是否被允许，是基于指定的域名（domain）和远程规则（remote pattern）。

```ts
import { isRemoteAllowed } from 'astro/assets/utils';

const testImageURL = 'https://example.com/images/test.jpg';
const domains = ['example.com', 'anotherdomain.com'];
const remotePatterns = [
  { protocol: 'https', hostname: 'images.example.com', pathname: '/**' }, // 允许该主机名下的任何路径
];

const url = new URL(testImageURL);
const isAllowed = isRemoteAllowed(url.href, { domains, remotePatterns });

console.log(`该远程图像可以被允许吗？${isAllowed}`);
```

### `matchHostname()`

<p>
**类型：** `(url: URL, hostname?: string, allowWildcard = false): boolean`<br />
<Since v="4.0.0" />
</p>

将给定 URL 的主机名与指定的主机名进行匹配，并为通配符规则提供可选的支持。

```ts
import { matchHostname } from 'astro/assets/utils';

const testURL = new URL('https://sub.example.com/path/to/resource');

// matchHostname 函数的用法示例
const hostnameToMatch = 'example.com';

// 不带有通配符进行匹配
const isMatchWithoutWildcard = matchHostname(testURL, hostnameToMatch);
console.log(`该主机名在不带有通配符时匹配吗？${isMatchWithoutWildcard}`); // 输出：false

// 带有通配符进行匹配
const isMatchWithWildcard = matchHostname(testURL, hostnameToMatch, true);
console.log(`该主机名在带有通配符时匹配吗？${isMatchWithWildcard}`); // 输出：true
```

### `matchPathname()`

<p>
**类型：** `(url: URL, pathname?: string, allowWildcard = false): boolean`<br />
<Since v="4.0.0" />
</p>

将给定 URL 的路径名与指定的规则（pattern）进行匹配，并为通配符规则提供可选的支持。

```ts
import { matchPathname } from 'astro/assets/utils';

const testURL = new URL('https://example.com/images/photo.jpg');

// 用于匹配的路径名示例
const pathnameToMatch = '/images/photo.jpg';

// 不带有通配符进行匹配
const isMatchWithoutWildcard = matchPathname(testURL, pathnameToMatch);
console.log(`该路径名在不带有通配符时匹配吗？${isMatchWithoutWildcard}`); // 输出：true

// 带有通配符进行匹配
const wildcardPathname = '/images/*';
const isMatchWithWildcard = matchPathname(testURL, wildcardPathname, true);
console.log(`该路径名在带有通配符时匹配吗？${isMatchWithWildcard}`); // 输出：true
```

### `matchPattern()`

<p>
**Type:** `(url: URL, remotePattern: RemotePattern): boolean`<br />
<Since v="4.0.0" />
</p>

评估给定的 URL 是否匹配指定的远程规则（基于协议、主机名、端口以及路径名）。

```ts
import { matchPattern } from 'astro/assets/utils';

const testURL = new URL('https://images.example.com/photos/test.jpg');

// 定义一个用于匹配 URL 的远程规则
const remotePattern = {
  protocol: 'https',
  hostname: 'images.example.com',
  pathname: '/photos/**', // 使用通配符以允许 /photos/ 下的所有文件
  port: '', // 可选：匹配任意端口，留空则为默认
};

// 验证该 URL 是否匹配远程规则
const isPatternMatched = matchPattern(testURL, remotePattern);

console.log(`该 URL 和远程规则匹配吗？${isPatternMatched}`); // 输出：true
```

### `matchPort()`

<p>
**类型：** `(url: URL, port?: string): boolean`<br />
<Since v="4.0.0" />
</p>

验证给定的 URL 端口是否匹配指定的端口。如果不提供端口，则返回 `true`。

```ts
import { matchPort } from 'astro/assets/utils';

const testURL1 = new URL('https://example.com:8080/resource');
const testURL2 = new URL('https://example.com/resource');

// matchPort 函数的用法示例
const portToMatch = '8080';

// 将 URL 与指定的端口进行匹配
const isPortMatch1 = matchPort(testURL1, portToMatch);
console.log(`与端口匹配吗？${isPortMatch1}`); // 输出：true

// 将 URL 与未指定的端口进行匹配（将假定为默认端口）
const isPortMatch2 = matchPort(testURL2, portToMatch);
console.log(`与端口匹配吗？${isPortMatch2}`); // 输出：false

// 在不明确提供端口的情况下检查 URL（如果端口为 undefined，则默认为 true）
const isPortMatch3 = matchPort(testURL1);
console.log(`与端口匹配吗（无指定端口）？${isPortMatch3}`); // 输出：true
```

### `matchProtocol()`

<p>
**类型：** `(url: URL, protocol?: string): boolean`<br />
<Since v="4.0.0" />
</p>

将提供的 URL 所采用的协议与指定的协议进行比较。

```ts
import { matchProtocol } from 'astro/assets/utils';

const testURL1 = new URL('https://example.com/resource');
const testURL2 = new URL('http://example.com/resource');

// matchProtocol 函数的用法示例
const protocolToMatch = 'https';

// 将 URL 和正确的协议进行匹配
const isProtocolMatch1 = matchProtocol(testURL1, protocolToMatch);
console.log(`该协议与 testURL1 匹配吗？${isProtocolMatch1}`); // 输出：true

// 将 URL 和错误的协议进行匹配
const isProtocolMatch2 = matchProtocol(testURL2, protocolToMatch);
console.log(`该协议与 testURL2 匹配吗？${isProtocolMatch2}`); // 输出：false

// 在不明确提供协议的情况下检查 URL（如果协议为 undefined，则默认为 true）
const isProtocolMatch3 = matchProtocol(testURL1);
console.log(`与该协议匹配吗（无指定协议）？${isProtocolMatch3}`); // 输出：true
```

### `isESMImportedImage()`

<p>
**类型：** `(src: ImageMetadata | string): boolean`<br />
<Since v="4.0.0" />
</p>

判断给定的图像来源是否为通过 ECMAScript 模块（ESM）导入的图像。

```ts
import { isESMImportedImage } from 'astro/assets/utils';

// isESMImportedImage 函数的用法示例
const imageMetadataExample = {
  src: '/images/photo.jpg',
  width: 800,
  height: 600,
  format: 'jpg',
};

const filePathExample = '/images/photo.jpg';

// 检查所置入的图像是否为 ESM 导入
const isMetadataImage = isESMImportedImage(imageMetadataExample);
console.log(`imageMetadataExample 是否为 ESM 所导入的图片？${isMetadataImage}`); // 输出：true

const isFilePathImage = isESMImportedImage(filePathExample);
console.log(`isFilePathImage 是否为 ESM 所导入的图片？${isFilePathImage}`); // 输出：false
```


### `isRemoteImage()`

<p>
**类型：** `(src: ImageMetadata | string): boolean`<br />
<Since v="4.0.0" />
</p>

判断给定的图像来源是否为字符串类型的远程图像 URL。

```ts
import { isRemoteImage } from 'astro/assets/utils';

// isRemoteImage 函数的用法示例
const remoteImageUrl = 'https://example.com/images/photo.jpg';
const localImageMetadata = {
  src: '/images/photo.jpg',
  width: 800,
  height: 600,
  format: 'jpg',
};

// 检查是否为远程图像 URL
const isRemote1 = isRemoteImage(remoteImageUrl);
console.log(`remoteImageUrl 是远程图像吗？${isRemote1}`); // 输出：true

const isRemote2 = isRemoteImage(localImageMetadata);
console.log(`localImageMetadata 是远程图像吗？${isRemote2}`); // 输出：false
```

### `resolveSrc()`

<p>
**类型：** `(src: UnresolvedImageTransform['src']): Promise<string | ImageMetadata>`<br />
<Since v="4.0.0" />
</p>

返回图像来源。该函数确保：如果 `src` 是 Promise（例如：通过动态 `import()` 导入的模块），则会等待其解析并获取最终的 `src` 值；如果 `src` 已经是已解析的值，则直接返回。

```ts
import { resolveSrc } from 'astro/assets/utils';
import localImage from "./images/photo.jpg";

const resolvedLocal = await resolveSrc(localImage);
// 将会是 `{ src: '/images/photo.jpg', width: 800, height: 600, format: 'jpg' }`

const resolvedRemote = await resolveSrc("https://example.com/remote-img.jpg");
// 将会是 `"https://example.com/remote-img.jpg"`

const resolvedDynamic = await resolveSrc(import("./images/dynamic-image.jpg"))
// 将会是 `{ src: '/images/dynamic-image.jpg', width: 800, height: 600, format: 'jpg' }`
```

### `imageMetadata()`

<p>
**类型：** `(data: Uint8Array, src?: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>`<br />
<Since v="4.0.0" />
</p>

从所提供的的图像提取其元数据，诸如尺寸，格式，方向。

```ts
import { imageMetadata } from 'astro/assets/utils';

async function extractImageMetadata() {
  // 图像数据示例（Uint8Array）
  const exampleImageData = new Uint8Array([/* ...二进制图像数据... */]);

  // 可选来源路径（可用于调试或其他元数据上下文）
  const sourcePath = '/images/photo.jpg';

  try {
    // 从图像数据提取元数据
    const metadata = await imageMetadata(exampleImageData, sourcePath);

    console.log('提取图像元数据', metadata);
    // 输出示例：
    // {
    //   width: 800,
    //   height: 600,
    //   format: 'jpg',
    //   orientation: undefined
    // }
  } catch (error) {
    console.error('从图像提取其元数据失败：', error);
  }
}

await extractImageMetadata();
```

### `emitESMImage()`

:::caution[已弃用]
使用 [`emitImageMetadata`](#emitimagemetadata) 函数进行替代。
:::

<p>
**类型：** `(id: string | undefined, _watchMode: boolean, experimentalSvgEnabled: boolean,  fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>`<br />
<Since v="4.0.0" />
</p>

处理图像文件并生成其元数据及可选内容。在构建模式下，该函数使用 `fileEmitter` 以生成资源引用；在开发模式下，函数解析为本地文件 URL，并在 URL 中添加元数据相关的查询参数。

```ts

import { emitESMImage } from 'astro/assets/utils';

const imageId = '/images/photo.jpg';
const unusedWatchMode = false; // 已弃用, 闲置
const unusedExperimentalSvgEnabled = false; // 仅在你使用 SVG 并希望将文件数据嵌入时，设置为 `true`

try {
  const result = await emitESMImage(imageId, unusedWatchMode, unusedExperimentalSvgEnabled);
  if (result) {
    console.log('图像元数据及其他内容：', result);
    // 输出示例：
    // {
    //   width: 800,
    //   height: 600,
    //   format: 'jpg',
    //   contents: Uint8Array([...])
    // }
  } else {
    console.log('该图像未生成元数据。');
  }
} catch (error) {
  console.error('生成 ESM 图像失败：', error);
}

```

### `emitImageMetadata()`

<p>
**类型：** `(id: string | undefined, fileEmitter?: FileEmitter): Promise<ImageMetadataWithContents | undefined>`<br />
<Since v="5.7.0" />
</p>

处理图像文件并生成其元数据，可选择性地输出其内容：在构建模式下，该函数通过 `fileEmitter` 来生成资源引用。在开发模式下，函数将其解析为本地文件 URL，并在 URL 中添加元数据相关的查询参数。

```ts

import { emitImageMetadata } from 'astro/assets/utils';

const imageId = '/images/photo.jpg';

try {
  const result = await emitImageMetadata(imageId);
  if (result) {
    console.log('图像元数据及内容', result);
    // Example output:
    // {
    //   width: 800,
    //   height: 600,
    //   format: 'jpg',
    //   contents: Uint8Array([...])
    // }
  } else {
    console.log('该图像没有生成元数据。');
  }
} catch (error) {
  console.error('无法生成 ESM 图像：', error);
}

```

### `getOrigQueryParams()`

<p>
**类型：** `(params: URLSearchParams): Pick<ImageMetadata, 'width' | 'height' | 'format'> | undefined`<br />
<Since v="4.0.0" />
</p>

从 [`URLSearchParams` 对象](https://developer.mozilla.org/zh-CN/docs/Web/API/URLSearchParams) 获取图片的 `width`、`height` 以及 `format`。如果其中任意一个参数缺失或无效，该函数都会返回 `undefined`。

```ts

import { getOrigQueryParams } from 'astro/assets/utils';

const url = new URL('https://example.com/image.jpg?width=800&height=600&format=jpg');
const queryParams = url.searchParams;

// 提取原始查询参数
const origParams = getOrigQueryParams(queryParams);

if (origParams) {
  console.log('原始查询参数：', origParams);
  // 输出示例：
  // {
  //   width: 800,
  //   height: 600,
  //   format: 'jpg'
  // }
} else {
  console.log('无法提取原始查询参数。');
}
```

### `inferRemoteSize()`

<p>
**类型：** `(url: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>`<br />
<Since v="4.0.0" />
</p>

通过流式传输远程图像数据并逐步分析其内容，以推断出图片的尺寸。此方法会在获取到足够的元数据时停止。

```ts

import { inferRemoteSize } from 'astro/assets/utils';

async function getRemoteImageSize() {
  const remoteImageUrl = 'https://example.com/image.jpg';

  try {
    // 从 URL 推断出远程图像的尺寸
    const imageSize = await inferRemoteSize(remoteImageUrl);

    console.log('推断出的远程图像尺寸：', imageSize);
    // 输出示例：
    // {
    //   width: 1920,
    //   height: 1080,
    //   format: 'jpg'
    // }
  } catch (error) {
    console.error('无法推断出远程图像的尺寸：', error);
  }
}

await getRemoteImageSize();
```

### `propsToFilename()`

<p>
**类型：** `(filePath: string, transform: ImageTransform, hash: string): string`<br />
<Since v="4.0.0" />
</p>

基于图像的源路径、转换属性和唯一的哈希值，为其生成格式化的文件名。

格式化的文件名遵循以下结构：

`<prefixDirname>/<baseFilename>_<hash><outputExtension>`

- `prefixDirname`：如果图像为 ESM 导入图像，则该项为原始文件路径的目录名；其他情况下，为空字符串。
- `baseFilename`：文件名的基本名称，如果文件为 `data:` 协议的 URI，则为哈希算法转换后的短名称。
- `hash`：生成唯一的哈希字符串，用以区分已转换的文件。
- `outputExtension`：从 `transform.format` 或原始文件扩展名得出的，所需输出文件扩展名。

```ts
import { propsToFilename } from 'astro/assets/utils';

function generateTransformedFilename() {
  const filePath = '/images/photo.jpg';
  const transform = {
    format: 'png',
    src: '/images/photo.jpg'
  };
  const hash = 'abcd1234';

  // 根据文件路径、转换属性和哈希值，以生成转换后的文件名
  const filename = propsToFilename(filePath, transform, hash);

  console.log('已生成的、转换后的文件名：', filename);
  // 输出示例：'/images/photo_abcd1234.png'
}

generateTransformedFilename();
```

### `hashTransform()`

<p>
**类型：** `(transform: ImageTransform, imageService: string, propertiesToHash: string[]): string`<br />
<Since v="4.0.0" />
</p>

基于选定的属性和指定的 `imageService`，将所提供的 `transform` 对象转换为哈希算法后的字符串。

```ts

import { hashTransform } from 'astro/assets/utils';

function generateTransformHash() {
  const transform = {
    width: 800,
    height: 600,
    format: 'jpg',
  };

  const imageService = 'astroImageService';
  const propertiesToHash = ['width', 'height', 'format'];

  // 根据转换属性、图像服务和属性以生成哈希
  const hash = hashTransform(transform, imageService, propertiesToHash);

  console.log('生成的转换哈希值：', hash);
  // 输出示例：'d41d8cd98f00b204e9800998ecf8427e'
}

generateTransformHash();
```
